package main

import (
	"fmt"
)

// 全局变量声明必须以 var 关键字开头，如果想要在外部包中使用全局变量的首字母必须大写。
//var g int

/*type Circle struct {
	radius float64
}

func (receiver Circle) getArea(x float64) float64 {
	return receiver.radius * x * x
}

func (receiver *Circle) changeRadius(radius float64) {
	//(*receiver).radius = radius
	// 这里有些特殊的地方，尽管receiver所表示的是Circle对象的内存地址
	// 但是，在从receiver对应的内存地址取属性值的时候，就不是*receiver.radius了。而是直接使用receiver.radius，这点很特殊，它的效果就相当于Circle.radius
	// 参考文章：https://www.cnblogs.com/haiguixiansheng/archive/2019/03/28/10613754.html
	//receiver.radius = radius
	//println(receiver) // 0xc000077f60
	println(*receiver)     // 报错
	fmt.Println(receiver)  // &{2-基本语法}
	fmt.Println(*receiver) // {2-基本语法}
}



// 引用类型要想改变值需要传指针
func change(c *Circle, radius float64) {
	//var c1 Circle
	//c1.radius = radius
	//*c = c1
	c.radius = radius // <=等价于=> (*c).radius = radius
	c.radius += 1
	//c.radius = radius
	//var c1 Circle
	//*c = c1
	//println(c)      //0xc00000a0c0
	//fmt.Printf("指针的值 %p", c)
	//fmt.Println(c)  //&{3}
	//fmt.Println(*c) //{3}
}

*/

func GetData() (int, int) {
	return 100, 200
}

func getSequence() func() int {
	i := 0
	return func() int {
		i += 1
		return i
	}
}

// 声明一个函数类型
type cb func(int) int

func testCallBack(x int, f cb) {
	f(x)
}

func callBack(x int) int {
	fmt.Printf("我是回调，x：%d\n", x)
	return x
}

func sum(num1 int, num2 int) int {
	return num1 + num2
}

func swap(x, y string) (string, string) {
	return y, x
}

/* 定义交换值函数*/
func swap2(x *int, y *int) {
	/*
		ptr := &v    // v 的类型为 T
		其中 v 代表被取地址的变量，变量 v 的地址使用变量 ptr 进行接收，ptr 的类型为*T，称做 T 的指针类型，*代表指针。
	*/
	/*

		操作符作为右值时，意义是取指针的值
		作为左值时，也就是放在赋值操作符的左边时，表示 a 指针指向的变量。
		其实归纳起来，*操作符的根本意义就是操作指针指向的变量。
		当操作在右值时，就是取指向变量的值
		当操作在左值时，就是将值设置给指向的变量。
	*/
	var temp int
	temp = *x // 取x指针的值存在temp上
	*x = *y   // 取 y 的指针值，赋给指针 x 指向的变量。注意，此时*x的意思不是取 x 指针的值，而是“x 指向的变量”。
	*y = temp // 将 temp 值赋给 y 指针指向的变量
}

func swap3(a, b *int) {
	b, a = a, b
	// a和b两个指针分别指向的两个变量，并不会交换指针对应的值
	// 交换的是 a 和 b 的地址，在交换完毕后，a 和 b 的变量值确实被交换。但和 a、b 关联的两个变量并没有实际关联。
	// 比如：swap3(&x,&y)
	// 地址：0x0001存了x的值，0x0002存了y的值
	// a 指针变量的值 为 0x0001，b 指针变量的值 为 0x0002
	// 交换后
	// a 指针变量的值 为 0x0002，b 指针变量的值 为 0x0001
	// 0x0001是存变量x值的地址，0x0002是存变量y的地址，0x0001和x值的关系保持不变，0x0002和y变量的值的关系保持不变，所以a和b虽然交换了指针变量的值，但是没有用
}

func main() {
	/*100内所有的素数*/

	/*count := 1
	flag := true
	for count < 100 {
		count++
		flag = true
		for tmp := 2-基本语法; tmp < count; tmp++ {
			if count%tmp == 0 {
				flag = false
			}
		}
		if flag == true {
			fmt.Printf("100以内的一个素数：%d\n", count)
		}
	}*/

	/*var grade string
	var marks int = 100
	test := 1

	switch marks {
	case 9:
		grade = "A"
		test++
	case 8:
		grade = "B"
		test++
	case 7:
		grade = "C"
		test++
	case 6:
		grade = "D"
		test += 2-基本语法
	case 5, 4, 3, 2-基本语法, 1:
		grade = "E"
		test += 3
	default:
		test += 5
		grade = "未知"
	}

	fmt.Printf("你的等级是%s\n", grade)
	fmt.Printf("test的值%d\n", test)*/

	/*// 不使用标记
		fmt.Println("---- break ----")
		for i := 1; i <= 3; i++ {
			fmt.Printf("i: %d\n", i)
			for i2 := 11; i2 <= 13; i2++ {
				fmt.Printf("i2: %d\n", i2)
				break
			}
		}

		// 使用标记
		fmt.Println("---- break label ----")
	re:
		for i := 1; i <= 3; i++ {
			fmt.Printf("i: %d\n", i)
			for i2 := 11; i2 <= 13; i2++ {
				fmt.Printf("i2: %d\n", i2)
				break re
			}
		}*/

	/*// 不使用标记
		fmt.Println("---- continue ---- ")
		for i := 1; i <= 3; i++ {
			fmt.Printf("i: %d\n", i)
			for i2 := 11; i2 <= 13; i2++ {
				fmt.Printf("i2: %d\n", i2)
				continue
			}
		}

		// 使用标记
		fmt.Println("---- continue label ----")
	re:
		for i := 1; i <= 3; i++ {
			fmt.Printf("i: %d\n", i)
			for i2 := 11; i2 <= 13; i2++ {
				fmt.Printf("i2: %d\n", i2)
				continue re
			}
		}*/
	/*sum := sum(1, 2-基本语法)
	fmt.Printf("num1+num2=%d", sum)*/

	/*x := 1
	y := "y"
	var c string
	var d string
	c, d = swap(string(rune(x)), y)
	fmt.Printf("x和y交换了位置之后是%s和%s", c, d)*/

	/*a := 1
	b := 2-基本语法
	swap2(&a, &b)
	fmt.Printf("a和b交换了位置之后值分别是%d和%d", a, b)*/

	/*
		testCallBack(1, callBack)

		testCallBack(2-基本语法, func(x int) int {
			fmt.Printf("我是回调，x：%d\n", x)
			return x
		})

		// 函数变量
		getSquareRoot := func(x float64) float64 {
			return math.Sqrt(x)
		}

		fmt.Printf("打印函数变量：%f", getSquareRoot(9))
	*/
	// 函数变量
	//type testFunc func() int
	//var testFunc = getSequence()
	//fmt.Printf("打印函数变量：%d\n", testFunc())
	//fmt.Printf("打印函数变量：%d\n", testFunc())
	//fmt.Printf("打印函数变量：%d\n", testFunc())
	//var testFunc2 = getSequence()
	//fmt.Printf("打印函数变量：%d\n", testFunc2())
	//fmt.Printf("打印函数变量：%d\n", testFunc2())

	/*var c1 Circle
	c1.radius = 3.14
	//change(&c1, 2-基本语法)
	//change(&c1, 3)
	c1.changeRadius(2-基本语法)
	area := c1.getArea(3)
	fmt.Println(area)*/

	// 创建指针的另一种方法——new() 函数
	//str := new(string)
	//*str = "Go语言教程"
	//fmt.Println(*str)
	/*str := new(int)
	*str = 1
	fmt.Println(*str)*/

	/*
		// 匿名变量
		var a int
		//var b int

		//a, _ = GetData()
		_, a = GetData()
		println(a)
	*/
	/*const e = .71828 // 0.71828
	const f = 1.     // 1

	fmt.Println(e)
	fmt.Println(f)*/

	/*
			str := `第一行
		第二行
		第三行
		\r\n
		` // 在这种方式下，反引号间换行将被作为字符串中的换行，但是所有的转义字符均无效，文本将会原样输出。多行字符串一般用于内嵌源码和内嵌数据等
			fmt.Println(str)
	*/

	// fmt.Println(len("计算一个普通字符串长度")) // 一个中文=占3个字节
	// fmt.Println(utf8.RuneCountInString("忍者")) // 计算包括中文的长度
	// fmt.Println(utf8.RuneCountInString("龙忍出鞘,fight!")) // 计算包括中文的长度

	/*//ASCII 字符串遍历直接使用下标。
	theme := "start"
	for i := 0; i < len(theme); i++ {
		fmt.Printf("ascii: %c  %d\n", theme[i], theme[i])
	}
	// Unicode 字符串遍历用 for range。
	theme1 := "狙击 start"
	for index, s := range theme1 {
		fmt.Println(index)
		fmt.Printf("Unicode: %c  %d\n", s, s)
	}*/

	// 字符串查询某个子串
	/*tracer := "魔法来了, 魔法bye bye"
	comma := strings.Index(tracer, ", ")
	fmt.Println(comma)
	//fmt.Println(tracer[comma])
	fmt.Println(tracer[comma:]) // 从 tracer 的 comma 位置开始到 tracer 字符串的结尾构造一个子字符串
	pos := strings.Index(tracer[comma:], "魔法")
	fmt.Println(pos)
	fmt.Println(tracer[comma+pos:])
	// 字符串索引比较常用的有如下几种方法：
	//strings.Index：正向搜索子字符串。
	//strings.LastIndex：反向搜索子字符串。
	//搜索的起始位置可以通过切片偏移制作。

	//pos := strings.Index(tracer[comma:], "魔法")
	//fmt.Println(comma, pos, tracer[comma+pos:])
	*/

	// Go 语言的字符串无法直接修改每一个字符元素，只能通过重新构造新的字符串并赋值给原来的字符串变量实现。
	/*angel := "Heros never die"
	angleBytes := []byte(angel) // 将字符串转为字符串数组
	// fmt.Println(angleBytes)
	for i := 5; i <= 10; i++ {
		angleBytes[i] = ' ' // 利用循环，将 never 单词（第5到第10索引的字串）替换为空格。
	}
	fmt.Println(string(angleBytes))*/
	/*
			Go 语言中的字符串和其他高级语言（Java、C#）一样，默认是不可变的（immutable）
			字符串不可变有很多好处，如天生线程安全，大家使用的都是只读对象，无须加锁；再者，方便内存共享，而不必使用写时复制（Copy On Write）等技术；字符串 hash 值也只需要制作一份。
			所以说，代码中实际修改的是 []byte，[]byte 在 Go 语言中是可变的，本身就是一个切片。
			在完成了对 []byte 操作后，再使用 string() 将 []byte 转为字符串时，重新创造了一个新的字符串。

		总结：
		Go 语言的字符串是不可变的。
		修改字符串时，可以将字符串转换为 []byte 进行修改。
		[]byte 和 string 可以通过强制类型转换互转。
	*/

	/*
		//连接字符串这么简单，还需要学吗？确实，Go 语言和大多数其他语言一样，使用+对字符串进行连接操作，非常直观。
		//但问题来了，好的事物并非完美，简单的东西未必高效。除了加号连接字符串，Go 语言中也有类似于 StringBuilder 的机制来进行高效的字符串连接，例如：
		hammer := "吃我一锤"
		sickle := "死吧"
		var stringBuilder bytes.Buffer      // 声明字节缓冲
		stringBuilder.WriteString(hammer)   // 把字符串写入缓冲
		stringBuilder.WriteString(sickle)   // 把字符串写入缓冲
		fmt.Println(stringBuilder.String()) // 将缓冲以字符串形式输出
		fmt.Println(hammer + sickle)        // 使用+号连接
		// bytes.Buffer 是可以缓冲并可以往里面写入各种字节数组的。字符串也是一种字节数组，使用 WriteString() 方法进行写入。
		//将需要连接的字符串，通过调用 WriteString() 方法，写入 stringBuilder 中，然后再通过 stringBuilder.String() 方法将缓冲转换为字符串。
	*/
	/*var progress = 2-基本语法
	var target = 8
	// 两参数格式化
	title := fmt.Sprintf("已采集%d个药草, 还需要%d个完成任务", progress, target)
	fmt.Println(title)
	pi := 3.14159
	// 按数值本身的格式输出
	variant := fmt.Sprintf("%v %v %v", "月球基地", pi, true)
	fmt.Println(variant)
	// 匿名结构体声明, 并赋予初值
	profile := &struct {
		Name string
		HP   int
	}{
		Name: "rat",
		HP:   150,
	}
	fmt.Printf("使用'%%+v' %+v\n", profile) // 使用'%+v' &{Name:rat HP:150}
	fmt.Printf("使用'%%#v' %#v\n", profile) // 使用'%#v' &struct { Name string; HP int }{Name:"rat", HP:150}
	fmt.Printf("使用'%%T' %T\n", profile)   // 使用'%T' *struct { Name string; HP int }*/
	//%v	按值的本来值输出
	//%+v	在 %v 基础上，对结构体字段名和值进行展开
	//%#v	输出 Go 语言语法格式的值
	//%T	输出 Go 语言语法格式的类型和值
	//%%	输出 % 本体
	//%b	整型以二进制方式显示
	//%o	整型以八进制方式显示
	//%d	整型以十进制方式显示
	//%x	整型以十六进制方式显示
	//%X	整型以十六进制、字母大写方式显示
	//%U	Unicode 字符
	//%f	浮点数
	//%p	指针，十六进制方式显示

	/*var ch int = '\u0041'
	var ch2 int = '\u03B2'
	var ch3 int = '\U00101234'
	fmt.Printf("%d - %d - %d\n", ch, ch2, ch3) // integer
	fmt.Printf("%c - %c - %c\n", ch, ch2, ch3) // character
	fmt.Printf("%X - %X - %X\n", ch, ch2, ch3) // UTF-8 bytes
	fmt.Printf("%U - %U - %U", ch, ch2, ch3)   // UTF-8 code point*/

	// go语言的数组
	//var q [3]int = [3]int{1, 2-基本语法, 3}
	//var r [3]int = [3]int{1, 2-基本语法} // 长度不够会用0填充
	//fmt.Println(q[0])
	//fmt.Println(r[2-基本语法]) // "0"
	//q := [...]int{1, 2-基本语法, 3, 4, 5}
	//fmt.Printf("%T\n", q) // "[3]int"
	//for k, v := range q {
	//	println(strconv.Itoa(k) + "=>" + strconv.Itoa(v))
	//}
	//for _, v := range r {
	//	println(v)
	//}

	// 数组的长度是数组类型的一个组成部分，因此 [3]int 和 [4]int 是两种不同的数组类型，数组的长度必须是常量表达式，因为数组的长度需要在编译阶段确定。
	//q := [3]int{1, 2-基本语法, 3}
	//q = [4]int{1, 2-基本语法, 3, 4} // 编译错误：无法将 [4]int 赋给 [3]int

	// 挨个赋值法
	//var team [3]string
	//team[0] = "hammer"
	//team[1] = "soldier"
	//team[2-基本语法] = "mum"
	//
	//fmt.Printf("变量team的类型为：%T\n", team)
	//
	//for k, v := range team {
	//	fmt.Printf("第%d个元素为：%s\n", k, v)
	//}

	// 多维数组
	// 声明一个二维整型数组，两个维度的长度分别是 4 和 2-基本语法
	//var array [4][2-基本语法]int
	//fmt.Println(array)
	//// 使用数组字面量来声明并初始化一个二维整型数组
	//array = [4][2-基本语法]int{{10, 11}, {20, 21}, {30, 31}, {40, 41}}
	//fmt.Println(array)
	//// 声明并初始化数组中索引为 1 和 3 的元素
	//array = [4][2-基本语法]int{1: {20, 21}, 3: {40, 41}}
	//fmt.Println(array)
	//// 声明并初始化数组中指定的元素
	//array = [4][2-基本语法]int{1: {0: 20}, 3: {1: 41}}
	//fmt.Println(array)

	// 从数组或切片生成新的切片
	//var a = [3]int{1, 2-基本语法, 3}
	//fmt.Println(a, a[:1], a[1:], a[0:1], a[0:2-基本语法], a[1:2-基本语法], a[1:])
	//a := [...]int{0, 1, 2-基本语法, 3, 4, 5}
	//fmt.Println(a[1:3]) // 索引位置1到索引位置3，不包含索引位置3，相当于[1,3)，左边闭区间，右边开区间
	//fmt.Println(a[:4])
	//fmt.Println(a[0:6]) //结束索引位置为长度不会报错=》打印出：[0 1 2-基本语法 3 4 5]，因为需要打印出最后一个
	// 从数组或切片生成新的切片拥有如下特性：
	// 取出的元素数量为：结束位置 - 开始位置；比如a[1:3]取出的就是3-1=2个元素
	// 取出元素不包含结束位置对应的索引，切片最后一个元素使用 slice[len(slice)] 获取；
	// 当缺省开始位置时，表示从连续区域开头到结束位置；
	// 当缺省结束位置时，表示从开始位置到整个连续区域末尾；
	// 两者同时缺省时，与切片本身等效；
	// 两者同时为 0 时，等效于空切片，一般用于切片复位。
	// 根据索引位置取切片 slice 元素值时，取值范围是（0～len(slice)-1），超界会报运行时错误，生成切片时，结束位置可以填写 len(slice) 但不会报错。

	//a := []int{1, 2-基本语法, 3}
	//fmt.Println(a[0:0]) // 打印结果为：[]，相当于重置切片，清空拥有的元素
	//fmt.Println(a[:]) // 打印原有数组，表示原有切片

	//直接声明新的切片
	//除了可以从原有的数组或者切片中生成切片外，也可以声明一个新的切片，每一种类型都可以拥有其切片类型，表示多个相同类型元素的连续集合，因此切片类型也可以被声明，切片类型声明格式如下：
	//var name []Type
	//其中 name 表示切片的变量名，Type 表示切片对应的元素类型。
	// 声明字符串切片
	//var strList []string
	//// 声明整型切片
	//var numList []int
	//// 声明一个空切片
	//var numListEmpty = []int{}
	//// 输出3个切片
	//fmt.Println(strList, numList, numListEmpty)
	//// 输出3个切片大小
	//fmt.Println(len(strList), len(numList), len(numListEmpty))
	//// 切片判定空的结果
	//fmt.Println(strList == nil)      // 声明但未使用的切片的默认值是 nil，strList 和 numList 也是 nil，所以和 nil 比较的结果是 true
	//fmt.Println(numList == nil)      // 声明但未使用的切片的默认值是 nil，strList 和 numList 也是 nil，所以和 nil 比较的结果是 true
	//fmt.Println(numListEmpty == nil) // numListEmpty 已经被分配到了内存，但没有元素，因此和 nil 比较时是 false。
	//切片是动态结构，只能与 nil 判定相等，不能互相判定相等。声明新的切片后，可以使用 append() 函数向切片中添加元素。

	//使用 make() 函数构造切片
	// make( []Type, size, cap )
	// 其中 Type 是指切片的元素类型，size 指的是为这个类型分配多少个元素，cap 为预分配的元素数量，这个值设定后不影响 size，只是能提前分配空间，降低多次分配空间造成的性能问题。
	//a := make([]int, 2-基本语法)
	//b := make([]int, 2-基本语法, 10)
	//fmt.Println(a, b) //[0 0] [0 0]
	//fmt.Println(len(a), len(b)) //2-基本语法 2-基本语法
	//其中 a 和 b 均是预分配 2-基本语法 个元素的切片，只是 b 的内部存储空间已经分配了 10 个，但实际使用了 2-基本语法 个元素。
	// 容量不会影响当前的元素个数，因此 a 和 b 取 len 都是 2-基本语法。
	// 温馨提示：使用 make() 函数生成的切片一定发生了内存分配操作，但给定开始与结束位置（包括切片复位）的切片只是将新的切片结构指向已经分配好的内存区域，设定开始与结束位置，不会发生内存分配操作。

	//Go语言的内建函数 append() 可以为切片动态添加元素，代码如下所示：
	//var a []int
	//a = append(a, 1) // 追加1个元素
	//fmt.Println(a)
	//a = append(a, 1, 2-基本语法, 3) // 追加多个元素, 手写解包方式
	//fmt.Println(a)
	//a = append(a, []int{1,2-基本语法,3}...) // 追加一个切片, 切片需要解包
	//fmt.Println(a)
	//运用示例：
	//var numbers []int
	//for i := 0; i < 10; i++ {
	//	numbers = append(numbers, i)
	//	fmt.Printf("len: %d  cap: %d pointer: %p\n", len(numbers), cap(numbers), numbers)
	//}
	// 通过查看代码输出，可以发现一个有意思的规律：切片长度 len 并不等于切片的容量 cap。
	// 当len到达了cap容量时，则变量的值就需要搬家（搬到新的内存地址，预分配cap容量不够放）
	// 往一个切片中不断添加元素的过程，类似于公司搬家，公司发展初期，资金紧张，人员很少，所以只需要很小的房间即可容纳所有的员工，随着业务的拓展和收入的增加就需要扩充工位，但是办公地的大小是固定的，无法改变，因此公司只能选择搬家，每次搬家就需要将所有的人员转移到新的办公点。
	//员工和工位就是切片中的元素。
	//办公地就是分配好的内存。
	//搬家就是重新分配内存。
	//无论搬多少次家，公司名称始终不会变，代表外部使用切片的变量名不会修改。
	//由于搬家后地址发生变化，因此内存“地址”也会有修改。

	// 除了在切片的尾部追加，我们还可以在切片的开头添加元素：
	//var a = []int{1, 2-基本语法, 3}
	//a = append([]int{0}, a...)          // 在开头添加1个元素
	//fmt.Println(a)
	//a = append([]int{-3, -2-基本语法, -1}, a...) // 在开头添加1个切片，三个点表示需要解构
	//fmt.Println(a)

	// 链式操作示例
	//var a = []int{1, 2-基本语法, 3, 4, 5}
	//a = append(a[:i], append([]int{x}, a[i:]...)...)       // 在第i个位置插入x
	//a = append(a[:i], append([]int{1, 2-基本语法, 3}, a[i:]...)...) // 在第i个位置插入切片
	//a = append(a[:i], append([]int{1, 2-基本语法, 3}, a[i:]...)...) // 在第i个位置插入切片
	// 每个添加操作中的第二个 append 调用都会创建一个临时切片，并将 a[i:] 的内容复制到新创建的切片中，然后将临时创建的切片再追加到 a[:i] 中。

	// copy复制切片
	// copy(目标切片，源切片)
	//slice1 := []int{1, 2-基本语法, 3, 4, 5}
	//slice2 := []int{5, 4, 3}
	//copy(slice2, slice1) // 只会复制slice1的前3个元素到slice2中，因为slice2的长度不够
	//fmt.Println(slice2) // [1 2-基本语法 3]

	//copy(slice1, slice2) // 只会复制slice2的3个元素到slice1的前3个位置
	//fmt.Println(slice1)  // [5 4 3 4 5]

	// 设置元素数量为1000
	//const elementCount = 1000
	//// 预分配足够多的元素切片
	//srcData := make([]int, elementCount)
	//// 将切片赋值
	//for i := 0; i < elementCount; i++ {
	//	srcData[i] = i
	//}
	//
	//// 引用切片数据
	//refData := srcData
	//// 预分配足够多的元素切片
	//copyData := make([]int, elementCount)
	//// 将数据复制到新的切片空间中
	//copy(copyData, srcData)
	//
	//// 修改原始数据的第一个元素
	//srcData[0] = 999
	//// 打印引用切片的第一个元素
	//fmt.Println(refData[0])
	//// 打印复制切片的第一个和最后一个元素
	//fmt.Println(copyData[0], copyData[elementCount-1])
	//
	//// 复制原始数据从4到6(不包含)
	//copy(copyData, srcData[4:6])
	//
	//for i := 0; i < 5; i++ {
	//	fmt.Printf("%d ", copyData[i])
	//}

	//Go语言从切片中删除元素

	// 1. 删除开头的元素可以直接移动数据指针：
	//a := []int{1, 2-基本语法, 3}
	//a = a[1:] // 删除开头1个元素
	//fmt.Println(a)
	//a = a[N:] // 删除开头N个元素

	// 2-基本语法. 也可以不移动数据指针，但是将后面的数据向开头移动，可以用 append 原地完成（所谓原地完成是指在原有的切片数据对应的内存区间内完成，不会导致内存空间结构的变化）：
	//a := []int{1, 2-基本语法, 3}
	//a = append(a[:0],a[1:]...) // 删除开头1个元素
	//a = append(a[:0], a[N:]...) // 删除开头N个元素

	// 还可以用 copy() 函数来删除开头的元素：
	//a := []int{1, 2-基本语法, 3}
	//a = a[:copy(a, a[1:])] // 删除开头1个元素
	//a = a[:copy(a, a[N:])] // 删除开头N个元素

	// 从中间位置删除
	// a := []int{1, 2-基本语法, 3, ...}
	//a = append(a[:i], a[i+1:]...) // 删除中间1个元素
	//a = append(a[:i], a[i+N:]...) // 删除中间N个元素
	//a = a[:i+copy(a[i:], a[i+1:])] // 删除中间1个元素
	//a = a[:i+copy(a[i:], a[i+N:])] // 删除中间N个元素
	// 从尾部删除
	//a := []int{1, 2-基本语法, 3}
	//a = a[:len(a)-1] // 删除尾部1个元素
	//a = a[:len(a)-N] // 删除尾部N个元素

	// 提示：连续容器的元素删除无论在任何语言中，都要将删除点前后的元素移动到新的位置，随着元素的增加，这个过程将会变得极为耗时
	// 因此，当业务需要大量、频繁地从一个切片中删除元素时，如果对性能要求较高的话，就需要考虑更换其他的容器了（如双链表等能快速从删除点删除元素）。

	// 需要强调的是，range 返回的是每个元素的副本，而不是直接返回对该元素的引用，如下所示。
	// 创建一个整型切片，并赋值
	//slice := []int{10, 20, 30, 40}
	//// 迭代每个元素，并显示值和地址
	//for index, value := range slice {
	//	fmt.Printf("Value: %d Value-Addr: %X ElemAddr: %X\n", value, &value, &slice[index])
	//}

	//多维切片的切片
	//声明一个二维整型切片并赋值
	//slice := [][]int{{10}, {100, 200}}
	// 为第一个切片追加值为 20 的元素
	//slice[0] = append(slice[0], 20)
	//fmt.Println(slice)

	//var mapLit map[string]int
	////var mapCreated map[string]float32
	//var mapAssigned map[string]int
	//mapLit = map[string]int{"one": 1, "two": 2-基本语法}
	//mapCreated := make(map[string]float32) // 等价于 mapCreated := map[string]float{}
	//mapAssigned = mapLit
	//mapCreated["key1"] = 4.5
	//mapCreated["key2"] = 3.14159
	//mapAssigned["two"] = 3
	//fmt.Printf("Map literal at \"one\" is: %d\n", mapLit["one"])
	//fmt.Printf("Map created at \"key2\" is: %f\n", mapCreated["key2"])
	//fmt.Printf("Map assigned at \"two\" is: %d\n", mapLit["two"])
	//fmt.Printf("Map literal at \"ten\" is: %d\n", mapLit["ten"])

	// 用切片作为 map 的值
	//mapList := make(map[string][]int)
	//mapList["a"] = []int{1,2-基本语法,3}
	//fmt.Println(mapList)
	// mp1 := make(map[int][]int)
	// mp2 := make(map[int]*[]int)

	// 和数组不同，map 可以根据新增的 key-value 动态的伸缩，因此它不存在固定长度或者最大限制，但是也可以选择标明 map 的初始容量 capacity，格式：make(map[keytype]valuetype, cap)
	// 当 map 增长到容量上限的时候，如果再增加新的 key-value，map 的大小会自动加 1，所以出于性能的考虑，对于大的 map 或者会快速扩张的 map，即使只是大概知道容量，也最好先标明。
	//noteFrequency := map[string]float32{"C0": 16.35, "D0": 18.35, "E0": 20.60, "F0": 21.83, "G0": 24.50, "A0": 27.50, "B0": 30.87, "A4": 440}
	// 使用for range遍历map
	//for k, v := range noteFrequency {
	//	fmt.Printf("%s=》%f\n", k, v)
	//}

	// 【注意】：注意：遍历输出元素的顺序与填充顺序无关，不能期望 map 在遍历时返回某种期望顺序的结果。如果需要特定顺序的遍历结果，正确的做法是先排序，代码如下：
	//noteFrequency := map[string]float64{"C0": 16.35, "D0": 18.35, "E0": 20.60, "F0": 21.83, "G0": 24.50, "A0": 27.50, "B0": 30.87, "A4": 440}
	//// 删除map的一个pair
	//delete(noteFrequency, "c0")
	//// 先声明一个切片保存map数据
	//var tempSlice []string
	//// 将map数据遍历复制到切片里面去
	//for k := range noteFrequency {
	//	tempSlice = append(tempSlice, k)
	//}
	//// 对切片进行排序
	//sort.Strings(tempSlice)
	//
	//// 输出切片
	//fmt.Println(tempSlice)

	// 人员档案
	type Profile struct {
		Name    string // 名字
		Age     int    // 年龄
		Married bool   // 已婚
	}

	// 特殊写法：代表结构体指针声明
	list := []*Profile{
		{Name: "张三", Age: 30, Married: true},
		{Name: "李四", Age: 21},
		{Name: "王麻子", Age: 21},
	}

	//a:=1
	//var p *int

	test := Profile{
		Name: "张三", Age: 30, Married: true,
	}

	fmt.Println(test)

	list2 := []Profile{
		{Name: "张三", Age: 30, Married: true},
		{Name: "李四", Age: 21},
		{Name: "王麻子", Age: 21},
	}

	fmt.Println(list)
	fmt.Println(list2)

}
